package cn.edu.hit.core.transfer;

import java.util.HashSet;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Logger;

public class TransferLoader {
    ServiceLoader<Transfer> serviceLoader;
    private static TransferLoader ourInstance = new TransferLoader();
    private static Logger logger = Logger.getLogger("TransferLoader");
    private Set<Transfer> transfers = new HashSet<>();

    private Set<ReduceTransfer> reduceTransfers = new HashSet<>();

    public static TransferLoader getInstance() {
        return ourInstance;
    }

    private TransferLoader() {
        init();
    }

    private void init() {
        serviceLoader = ServiceLoader.load(Transfer.class);
        serviceLoader.forEach(transfer -> {
            System.out.println("TransferLoader load " + transfer.getClass().getName());
            transfers.add(transfer);
        });
        System.out.println("TransferLoader load count " + transfers.size());

        ServiceLoader<ReduceTransfer> reduceLoader = ServiceLoader.load(ReduceTransfer.class);
        reduceLoader.forEach(transfer -> reduceTransfers.add(transfer));
    }

    private ConcurrentMap<String, Transfer> transferConcurrentMap = new ConcurrentHashMap<>();
    private ConcurrentMap<String, ReduceTransfer> reduceTransferConcurrentMap = new ConcurrentHashMap<>();

    public Transfer getTransfer(String name) {
        if (transferConcurrentMap.containsKey(name)) {
            return transferConcurrentMap.get(name);
        }

        Transfer transfer = transfers.parallelStream().filter(t -> t.accept(name)).findAny().orElse(defaultTransfer);
        if (defaultTransfer == transfer) {
            if (!"1to1".equals(name))
                System.out.println("Cannot find Loader of " + name);
//            serviceLoader.reload();
//            serviceLoader.forEach(transfer1->{
//                System.out.println("TransferLoader load "+transfer1.getClass().getName());
//                transfers.add(transfer1);
//            });
        } else {
            System.out.println(name + ":" + transfer.getClass().getName());
            transferConcurrentMap.put(name, transfer);
        }
        return transfer;
    }

    public void addTransfer(String type, Transfer transfer) {
        if (!this.hasTransfer(type)) {
            transfers.add(transfer);
        }
    }

    public boolean hasTransfer(String type) {
        return transfers.stream().filter(adapter -> adapter.accept(type)).findAny().isPresent();
    }

    public ReduceTransfer getReduceTransfer(String name) {
        if (transferConcurrentMap.containsKey(name)) {
            return reduceTransferConcurrentMap.get(name);
        }
        ReduceTransfer transfer = reduceTransfers.parallelStream().filter(t -> t.accept(name)).findAny().orElse(defaultReduceTransfer);
        reduceTransferConcurrentMap.put(name, transfer);
        return transfer;
    }

    public static Transfer get(String name) {
        return ourInstance.getTransfer(name);
    }

    public static ReduceTransfer getReduce(String name) {
        return ourInstance.getReduceTransfer(name);
    }

    private Transfer defaultTransfer = (x, arg) -> x;

    private ReduceTransfer defaultReduceTransfer = (x, y, arg) -> x;

    public static void main(String[] args) {
        TransferLoader loader = TransferLoader.getInstance();
        loader.transfers.forEach(transfer -> {
            System.out.println(transfer.getClass().getName());
        });

        Transfer fft = loader.getTransfer("fft");
        System.out.println(fft);
    }
}
